package demo1;

//类是一种数据类型，对象是具体的数据，类中封装了数据和方法
//类是模板，对象是实例

//类就是对事物的描述，对物体的抽象，而对象是具体事物，也叫实例
//类中包括物体的属性和行为，属性就是数据，行为就是方法
//因此定义一个类，就是定义一个事物，需要定义事物有哪些属性和行为
//定义类的核心就是抽象能力

//类名用大驼峰形式
//一个源文件里可以有多个类，但只能有一个public类，且public类名与源文件名相同
//但习惯上在工程里一个源文件里只写一个类
//就像我现在写的这个源文件，里面有三个类，在idea上就自动帮我分成了三个类

//创建一个狗类
class Dog {
    String name;
    int age;
    String color;
    //类中定义的变量叫做成员变量，类中定义的方法叫做成员方法
    void eat() {
        //区分成员变量和局部变量
        //在方法内部，定义的变量，就是局部变量
        String food = "狗粮";
        System.out.println("正在吃" + food);
    }
    void sleep() {
        System.out.println("小狗正在睡觉");
    }
    //构造方法
    //构造方法既参与创建对象的过程，也负责对对象的初始化
    public Dog() {
        System.out.println("正在实例化一个狗对象");
        //可以在构造方法中，对成员变量进行初始化
        this.name = "哈士奇";
        this.age = 3;
        this.color = "black and while";
        //在构造方法中，也可以调用成员方法
        this.eat();
    }
    //注意！！！构造方法不负责给对象开辟空间，实例化一个对象的步骤是：
    //1.创建一个对象，在堆内存中开辟一个空间
    //2.调用合适的构造方法，为对象成员变量进行初始化
    //3.返回对象引用，指向堆内存空间
    //在构造方法中，可以调用其他构造方法，但是只能调用本类的构造方法，不能调用其他类的构造方法
    //如果不定义构造方法，编译器会默认定义一个无参的构造方法
    //类似于
    //Dog() {
    // }
    //也可以定义多个构造方法，构造方法重载
    public Dog(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }
}



//this关键字
//this关键字代表当前对象，this关键字可以出现在成员方法中，也可以出现在成员变量中
//this表示当前对象的引用，如this.name表示当前对象的name成员变量，this.eat()表示当前对象的eat成员方法
class Cat {
    String name;
    int age;
    String color;
    void eat() {
        System.out.println(this.name + "正在吃鱼");
    }
    void sleep() {
        System.out.println(this.name + "正在睡觉");
    }
    public Cat(String name, int age, String color) {
        this();
        //上面的用法是调用同一类中的其他构造方法，this()必须放在构造方法的第一行，调用构造方法时，必须放在第一行，否则编译不通过
        //其实上面这样调用是用来简化代码的，如把对成员变量的复制放在下面的构造方法里
        //下面的this就非常关键了，可以区分成员变量和局部变量，形参是局部变量，这里要把形参的值赋给成员变量
        this.name = name;
        this.age = age;
        this.color = color;
    }
    public Cat() {
        //要注意两个构造方法不能同时使用this(),否则无限循环，代码崩溃
        System.out.println("猫娘最可爱了！");
    }
}
//关于this与构造方法
//当你显式定义一个构造方法时，虽然你不需要显式地写出this关键字，但this在构造方法的执行过程中是隐含存在的。
//this会在成员方法中隐式引用当前对象，作为方法调用的前缀，所以我们一般都不手写this.成员方法名



//类的初始化方法
//1. 默认初始化
//   int - 0  float - 0.0f  double - 0.0  long - 0L  short - 0  byte - 0  boolean - false  char - '\u0000'
//2. 就地初始化
//   如Dog里定义成员变量时直接赋值，Dog.name = "哈士奇";
//   这种初始化方法加上封装后，就直接写死了，不能修改，所以不建议使用
//3. 构造方法初始化
//4. 代码块初始化



//static关键字详解
//例：
class Student {
    static String classroom ;
    String name;
    int age;
    static {
        classroom = "116";
    }
    public static String getClassroom() {
        return Student.classroom;
        //注意，这里不能使用this.classroom，因为静态成员变量是属于类的，不依赖于对象，不能使用this，只能使用类名.成员变量名
    }
}
//1. static 修饰成员变量，表示成员变量是静态的，静态成员变量属于类，不属于对象
//  这里的用法就是假如有三个同班同学，在描述他们时，实例化三个Student对象，每次初始化都要设置classroom = "1班";
//  非常繁琐，所以static修饰，这样classroom就属于类，不属于对象，这样不用每次初始化都设置classroom = "1班"；
//  静态成员变量，在类加载时进行初始化，只初始化一次。
//  静态成员变量的特性：
//  (1)不属于某个具体的对象，是类的属性，所有对象共享的，不存储在某个对象的空间中(！！！最大的特点！！！)
//  (2)既可以通过对象访问，也可以通过类名访问，但一般更推荐使用类名访问
//  (3)类变量存储在方法区当中
//  (4)生命周期伴随类的一生
//2. static 修饰成员方法，表示成员方法也是静态的，静态成员方法属于类，不属于对象
//  上面的getClassroom()就是一个静态成员方法，静态成员方法在类加载时进行初始化，只初始化一次。
//  静态成员方法的特性：
//  (1) 被static修饰的成员方法称为静态成员方法，是类的方法，不是某个对象所特有的。
//  (2) 可以通过对象调用，也可以通过类名.静态方法名(...)方式调用，更推荐使用后者
//  (3) 静态成员方法可以访问静态成员变量，不能直接访问非静态成员变量, 不能在静态方法中直接访问任何非静态成员变量
//      这句话意思是不能直接用变量名，但可以使用类名.成员变量名访问非静态成员变量
//  (4) 静态方法中不能调用任何非静态方法，因为非静态方法有this参数，在静态方法中调用时候无法传递this引用
//      意思是非静态方法名前面其实都隐含一个this参数，在静态方法中调用非静态方法时无法传递this参数，所以静态方法中不能调用非静态方法
//  (5) 静态方法无法重写，不能用来实现多态(此处大家暂时不用管，后序多态位置详细讲解)。
//static关键字的注意事项：被static修饰的一定要要用类名.成员变量/方法名访问，不能使用this.成员变量/方法名访问



//代码块
//1. 普通代码块：定义在方法中的代码块
//  就是直接使用{}引起来的代码块，这种用法较少见
//2. 构造代码块：定义在构造方法中的代码块
//  构造代码块在构造方法执行时，自动执行，而且优先于构造方法执行，而且可以多次执行
//  这种方法一般用来初始化示例成员变量
//3. 静态代码块：定义在类中使用static修饰的代码块
//  静态代码块在类加载时执行，而且不管实例化几个对象，只执行一次
//  如果一个类中包含多个静态代码块，在编译代码时，编译器会按照定义的先后次序依次执行(合并)
//  一般用于初始化静态成员变量
//比较：实例代码块只有在创建对象时才会执行，而静态代码块是类的属性，因此是在JVM加载类时开辟空间并初始化的。
//执行顺序：静态代码块 -> 构造代码块 -> 构造方法